#!/usr/bin/python
# -*- coding: UTF-8 -*-
import qrcode
import requests
import json
from PIL import Image
import threading
import time
from tqdm import tqdm
import ffmpy3
import os
clsCmd='cls'
if os.name!='nt':
    clsCmd='clear'
LOGIN_QRCODE_URL = "http://passport.bilibili.com/qrcode/getLoginUrl"
LOGIN_INFO_URL = "http://passport.bilibili.com/qrcode/getLoginInfo"
VIDEO_INFO_URL = "http://api.bilibili.com/x/web-interface/view"
VIDEO_STREAM_URL = "http://api.bilibili.com/x/player/playurl"
VideoInfoErr = {-400: "请求错误", -403: "权限不足", -404: "无视频", 62002: "稿件不可见"}
VideoQN = {6: '240P 极速', 16: '360P 流畅', 32: '480P 清晰', 64: '720P 高清（登录）', 74: '720P60 高清（大会员）', 80: '1080P 高清（登录）',
           112: '1080P+ 高清（大会员）', 116: '1080P60 高清（大会员）', 120: '4K 超清（大会员）'}
session = requests.session()
headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36",
    "referer": "https://www.bilibili.com/",
    "origin": "https://www.bilibili.com",
    "accept": "*/*",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "zh-CN,zh;q=0.9"}

isLogin = False
isFailed = False
selected = ''
cids = []
pages = []


def getQRcode():
    resp = session.get(LOGIN_QRCODE_URL)
    rjs = json.loads(resp.text)
    codeUrl = rjs['data']['url']
    oKey = rjs['data']['oauthKey']  # 获取oauthKey用于鉴权
    qr = qrcode.QRCode()
    qr.add_data(codeUrl)
    img = qr.make_image()  # 制作二维码
    img.show()  # 调用PIL显示
    return oKey


def checkLoginStatus(key):
    global isLogin
    resp = session.post(LOGIN_INFO_URL, {'oauthKey': key})
    rjs = json.loads(resp.text)
    if (rjs['status']):
        print("登陆成功")
        isLogin = True
    else:
        print("Wait...")


def loginFailed():
    global isFailed
    print("错误！超时！")
    isFailed = True


def login():
    key = getQRcode()
    # th=threading.Thread(target=checkLoginStatus)
    # th.start()
    th = threading.Timer(180, loginFailed)  # 定时器 180s倒计时
    th.start()  # 开定时器
    checkLoginStatus(key)  # 先检查一次登陆状态
    while (not (isLogin or isFailed)):  # 在没有登录成功或超时时阻塞进程
        time.sleep(10)
        checkLoginStatus(key)


def getVideoURL():
    if (len(selected) == 0):
        print("Please select one video first!")
        return
    if not (getCids()):
        return
    arr = []
    info()
    userinput = input("输入下载的P数（可以为区间，例1-2，可以多选，例1,3,5 请使用英文逗号！）")
    if '-' in userinput:
        tmp = userinput.split('-')
        for i in range(int(tmp[0]), int(tmp[1]) + 1):
            arr.append(i)
    elif ',' in userinput:
        tmp = userinput.split(',')
        for i in tmp:
            arr.append(int(i))
    else:
        arr.append(int(userinput))
    if (selected[0:2].lower() == 'av'):
        data = "?avid=" + selected[2:] + "&"
    else:
        data = "?bvid=" + selected + "&"
    resps = []
    for i in arr:
        cid = cids[i - 1]
        dd = data + "cid=" + cid + "&fnval=16"
        resps.append([i, json.loads(session.get(VIDEO_STREAM_URL + dd).text)])
    for t in resps:
        video = t[1]['data']['dash']['video'][1]['baseUrl']
        audio = t[1]['data']['dash']['audio'][0]['baseUrl']
        v = session.get(video, headers=headers, stream=True)
        bar = tqdm(total=int(v.headers["content-length"]), desc="VideoStream")
        with open("download\\t" + pages[t[0] - 1]['part'] + '.mp4', 'wb') as f:
            for chunk in v.iter_content(chunk_size=512):
                if chunk:
                    bar.update(512)
                    f.write(chunk)

        a = session.get(audio, headers=headers, stream=True)
        bar = tqdm(total=int(a.headers["content-length"]), desc="AudioStream")
        with open("download\\t" + pages[t[0] - 1]['part'] + '.m4s', 'wb') as f:
            for chunk in a.iter_content(chunk_size=512):
                if chunk:
                    bar.update(512)
                    f.write(chunk)
        pname="download\\t" + pages[t[0] - 1]['part']
        oname="download\\" + pages[t[0] - 1]['part']
        ffmpy3.FFmpeg(inputs={pname + '.mp4': None,
                              pname + '.m4s': None},
                      outputs={oname + '.mp4': '-y -c:v copy -c:a copy'}).run()
        os.remove(pname + '.m4s')
        os.remove(pname + '.mp4')
    return


def getCids():
    global cids
    if (selected[0:2].lower() == 'av'):
        data = "?aid=" + selected[2:]  # 通过AVID请求时 需要去掉AV的前缀
    else:
        data = "?bvid=" + selected
    resp = session.get(VIDEO_INFO_URL + data)
    rjs = json.loads(resp.text)
    if (rjs['code'] != 0):
        print("错误！" + VideoInfoErr[rjs['code']])  # 错误，未能返回
        return False
    pages = rjs['data']['pages']
    for i in pages:
        cids.append(str(i['cid']))
    return True


def info():
    global pages
    if (len(selected) == 0):
        print("Please select one video first!")
        return
    if (selected[0:2].lower() == 'av'):
        data = "?aid=" + selected[2:]
    else:
        data = "?bvid=" + selected
    resp = session.get(VIDEO_INFO_URL + data)
    rjs = json.loads(resp.text)
    # print(rjs)
    if (rjs['code'] == 0):
        print("标题:" + rjs['data']['title'])
        print("发布日期:" + time.strftime('%Y-%m-%d %H:%M:%S',
                                      time.localtime(rjs['data']['pubdate'])))
        print("UP主:" + rjs['data']['owner']['name'])
        print("简介:" + rjs['data']['desc'])
        print("分P信息：")
        pages = rjs['data']['pages']
        # print(pages)
        for i in pages:
            print("|-->" + str(i['page']) + ":" + str(i['part']))
        return
    else:
        print("Error,cannot get info")

def printHelp():
    print("Usage:直接输入BV或AV号选中视频\nlogin 登录\ninfo 获取视频基本信息\ndownload/down/d 下载视频\nhelp 显示本提示")

DICT = {'login': login, 'info': info, 'download': getVideoURL,
        'down': getVideoURL, 'd': getVideoURL,'help':printHelp}


if __name__ == '__main__':
    if not(os.path.exists("download")):
        os.mkdir("download")
    while (1):
        cmd = input("###> ")
        os.system(clsCmd)
        tmp = cmd
        cmd = cmd.strip()
        cmd = cmd.lower()
        if (not (cmd in DICT)):
            if (cmd == 'quit'):
                break
            elif (cmd[0:2] == 'bv' or cmd[0:2] == 'av'):
                selected = tmp
                print(tmp + " has been Selected")
                continue
            else:
                print("Unkonw command")
                #os.system(clsCmd)
                printHelp()
                continue
        DICT[cmd]()
